3 # The Dictionary class is a Hash that preserves order.
4 # So it has some array-like extensions also. By defualt
5 # a Dictionary object preserves insertion order, but any
6 # order can be specified including alphabetical key order.
10 # Just require this file and use Dictionary instead of Hash.
13 # hsh = Dictionary.new
17 # p hsh.keys #=> ['z','a','c']
19 # # or using Dictionary[] method
20 # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
21 # p hsh.keys #=> ['z','a','c']
23 # # but this don't preserve order
24 # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
25 # p hsh.keys #=> ['a','c','z']
27 # # Dictionary has useful extensions: push, pop and unshift
28 # p hsh.push('to_end', 15) #=> true, key added
29 # p hsh.push('to_end', 30) #=> false, already - nothing happen
30 # p hsh.unshift('to_begin', 50) #=> true, key added
31 # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
32 # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
33 # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
34 # p hsh.keys #=> ["to_begin", "a", "c", "z"]
35 # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
39 # * You can use #order_by to set internal sort order.
40 # * #<< takes a two element [k,v] array and inserts.
41 # * Use ::auto which creates Dictionay sub-entries as needed.
42 # * And ::alpha which creates a new Dictionary sorted by key.
51 # * Andrew Johnson (merge, to_a, inspect, shift and Hash[])
52 # * Jeff Sharpe (reverse and reverse!)
53 # * Thomas Leitner (has_key? and key?)
55 # Originally ported from OrderHash 2.0, Copyright (c) 2005 jan molic
60 # ** Fixed initialize so the constructor blocks correctly effected dictionary rather then just the internal hash.
64 # Copyright (c) 2005 Jan Molic, Thomas Sawyer
68 # This module is free software. You may use, modify, and/or redistribute this
69 # software under the same terms as Ruby.
71 # This program is distributed in the hope that it will be useful, but WITHOUT
72 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
73 # FOR A PARTICULAR PURPOSE.
78 # The Dictionary class is a Hash that preserves order.
79 # So it has some array-like extensions also. By defualt
80 # a Dictionary object preserves insertion order, but any
81 # order can be specified including alphabetical key order.
85 # Just require this file and use Dictionary instead of Hash.
88 # hsh = Dictionary.new
92 # p hsh.keys #=> ['z','a','c']
94 # # or using Dictionary[] method
95 # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
96 # p hsh.keys #=> ['z','a','c']
98 # # but this don't preserve order
99 # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
100 # p hsh.keys #=> ['a','c','z']
102 # # Dictionary has useful extensions: push, pop and unshift
103 # p hsh.push('to_end', 15) #=> true, key added
104 # p hsh.push('to_end', 30) #=> false, already - nothing happen
105 # p hsh.unshift('to_begin', 50) #=> true, key added
106 # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
107 # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
108 # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
109 # p hsh.keys #=> ["to_begin", "a", "c", "z"]
110 # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
114 # * You can use #order_by to set internal sort order.
115 # * #<< takes a two element [k,v] array and inserts.
116 # * Use ::auto which creates Dictionay sub-entries as needed.
117 # * And ::alpha which creates a new Dictionary sorted by key.
125 # TODO is this needed? Doesn't the super class do this?
132 elsif (args.size % 2) != 0
133 raise ArgumentError, "odd number of elements for Hash"
136 hsh[args.shift] = args.shift
142 # Like #new but the block sets the order.
144 def new_by(*args, &blk)
145 new(*args).order_by(&blk)
148 # Alternate to #new which creates a dictionary sorted by key.
150 # d = Dictionary.alpha
154 # d #=> {"x"=>3,"y"=>2,"z"=>2}
156 # This is equivalent to:
158 # Dictionary.new.order_by { |key,value| key }
160 def alpha(*args, &block)
161 new(*args, &block).order_by_key
164 # Alternate to #new which auto-creates sub-dictionaries as needed.
166 # d = Dictionary.auto
167 # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
170 #AutoDictionary.new(*args)
171 leet = lambda { |hsh, key| hsh[key] = new(&leet) }
178 def initialize(*args, &blk)
182 dict = self # This ensure autmatic key entry effect the
183 oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash.
184 @hash = Hash.new(*args, &oblk)
186 @hash = Hash.new(*args)
195 # Keep dictionary sorted by a specific sort order.
197 def order_by( &block )
203 # Keep dictionary sorted by key.
205 # d = Dictionary.new.order_by_key
209 # d #=> {"x"=>3,"y"=>2,"z"=>2}
211 # This is equivalent to:
213 # Dictionary.new.order_by { |key,value| key }
215 # The initializer Dictionary#alpha also provides this.
218 @order_by = lambda { |k,v| k }
223 # Keep dictionary sorted by value.
225 # d = Dictionary.new.order_by_value
229 # d #=> {"x"=>3,"y"=>2,"z"=>2}
231 # This is equivalent to:
233 # Dictionary.new.order_by { |key,value| value }
236 @order_by = lambda { |k,v| v }
245 assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
246 @order = assoc.collect{ |k,v| k }
252 # return false if @order != hsh2.order
257 if hsh2.is_a?( Dictionary )
258 @order == hsh2.order &&
259 @hash == hsh2.instance_variable_get("@hash")
270 @hash.fetch(k, *a, &b)
277 # Or with additional index.
279 # h[key,index] = value
281 def []=(k, i=nil, v=nil)
295 @order.push( a ) unless @hash.has_key?( a )
310 order.each { |k| yield( k ) }
315 order.each { |k| yield( @hash[k] ) }
320 order.each { |k| yield( k,@hash[k] ) }
326 order.clone.each { |k| delete k if yield(k,@hash[k]) }
332 order.each { |k| ary.push @hash[k] }
341 hsh2 = self.class.new
342 order.each { |k| hsh2[@hash[k]] = k }
347 self.dup.delete_if(&block)
350 def reject!( &block )
351 hsh2 = reject(&block)
352 self == hsh2 ? nil : hsh2
362 key ? [key,delete(key)] : super
366 unless @hash.include?( k )
380 unless @hash.include?( k )
391 key ? [key,delete(key)] : nil
396 each {|k,v| ary << k.inspect + "=>" + v.inspect}
397 '{' + ary.join(", ") + '}'
402 each{ |k,v| a << k; a << v }
407 hsh2.each { |k,v| self[k] = v }
414 self.dup.update(hsh2)
419 each { |k,v| ary << [k,v] if yield k,v }
434 return @hash[order.first] unless x
435 order.first(x).collect { |k| @hash[k] }
440 return @hash[order.last] unless x
441 order.last(x).collect { |k| @hash[k] }
463 each { |k,v| ary << [k,v] }